Docker 기반 OpenProject 설치 및 운영

Docker 기반 OpenProject 설치 및 운영

1. OpenProject Docker Compose 배포 개요

1.1 서론

본 안내서는 Docker Compose를 활용하여 OpenProject를 안정적으로 설치하고, 장기적인 관점에서 운영하는 데 필요한 모든 기술적 절차와 심층적인 통찰을 제공하는 것을 목표로 한다. 이 문서는 Docker, Docker Compose, 그리고 외부 Nginx Proxy Manager가 이미 구축되어 있고, 사용자가 이에 대한 기본적인 지식을 보유하고 있다는 전제하에 작성되었다.

안내서의 핵심 내용은 다음과 같다. 첫째, Docker Compose를 이용한 OpenProject의 체계적인 설치 절차를 다룬다. 둘째, 커스텀 Docker 이미지 빌드를 통해 OpenProject의 기능을 확장하는 플러그인 설치 방법을 상세히 설명한다. 셋째, 백업, 복원, 업그레이드, 문제 해결 등 실무 운영에 필수적인 유지보수 전략을 포괄적으로 제시한다. 이를 통해 단순한 설치를 넘어, 프로덕션 환경에서 OpenProject를 안정적이고 효율적으로 운영할 수 있는 역량을 확보하게 될 것이다.

1.2 Docker Compose 기반 배포의 아키텍처 이해

OpenProject의 Docker 기반 배포 방식은 크게 두 가지로 나뉜다: 모든 기능이 하나의 컨테이너에 포함된 ‘all-in-one’ 방식과 각 프로세스가 개별 컨테이너로 분리된 ‘one container per process’ 방식이다.1 공식 문서에서는 후자인 ‘one container per process’ 방식을 강력히 권장하며, 본 안내서 역시 이 방식을 기준으로 한다.2 이 아키텍처는 각 컴포넌트의 독립성을 보장하여 확장성, 모니터링, 유지보수 측면에서 월등한 유연성과 안정성을 제공한다.

docker-compose.yml 파일에 정의된 각 서비스는 OpenProject 생태계를 구성하는 핵심 요소들이며, 그 역할은 다음과 같다 3:

  • db: PostgreSQL 데이터베이스 컨테이너. 모든 프로젝트 데이터, 사용자 정보, 작업 패키지, 설정 등 OpenProject의 상태(state)를 저장하는 가장 중요한 컴포넌트다. 데이터의 무결성과 가용성이 시스템 전체의 안정성을 좌우한다.

  • cache: Memcached 컨테이너. 자주 사용되는 데이터를 메모리에 임시 저장하여 데이터베이스 조회 부하를 줄이고 애플리케이션의 전반적인 응답 속도를 향상시키는 캐싱 레이어 역할을 수행한다.

  • web: Ruby on Rails 기반의 OpenProject 애플리케이션 서버(Puma)를 실행하는 메인 컨테이너. 사용자의 웹 요청을 직접 처리하고, 비즈니스 로직을 수행하며, 동적 웹 페이지를 생성하는 핵심적인 역할을 담당한다.

  • worker: 백그라운드 작업을 비동기적으로 처리하는 컨테이너. 이메일 알림 발송, 대용량 데이터 가져오기/내보내기, 웹훅(webhook) 전송 등 즉각적인 응답이 필요 없는 시간이 오래 걸리는 작업을 전담한다. 이를 통해 web 컨테이너는 사용자 요청 처리에만 집중할 수 있어 시스템 전체의 반응성을 유지할 수 있다.

  • cron: 정기적으로 실행되어야 하는 작업을 스케줄링하고 실행하는 컨테이너. 오래된 세션 정리, 정기 리포트 생성 등 주기적인 관리 작업을 담당한다.

  • seeder: 스택이 처음 시작될 때 데이터베이스 스키마 마이그레이션을 수행하고, 초기 관리자 계정 생성 등 기본 데이터를 채우는(seeding) 일회성 작업 컨테이너. 업그레이드 시에도 새로운 스키마 변경을 적용하는 중요한 역할을 한다.

  • proxy: OpenProject 스택 내부에 포함된 기본 프록시 컨테이너. 외부 요청을 받아 적절한 web 컨테이너로 전달하는 역할을 한다. 본 안내서의 구성에서는 이 컨테이너의 포트 매핑을 조정하여 외부 Nginx Proxy Manager와 연동하게 된다.

1.3 권장 시스템 사양 및 확장성 고려사항

OpenProject를 원활하게 운영하기 위한 시스템 사양은 사용 규모와 작업 부하에 따라 달라진다. 초기 구축 및 장기적인 확장 계획을 위해 다음의 가이드라인을 참고하라.

1.3.1 최소 사양

소규모 팀(약 200명 이하)이나 테스트 환경을 위한 최소 하드웨어 요구사항은 다음과 같다 5:

  • CPU: 쿼드 코어 CPU (2 GHz 이상)

  • Memory: 4 GB RAM

  • Disk Space: 20 GB 이상의 여유 공간

이는 기본적인 기능을 사용하는 데 무리가 없는 수준이지만, 동시 접속자 수가 많거나 첨부 파일 사용이 빈번할 경우 성능 저하가 발생할 수 있다.

1.3.2 확장 전략

사용자 수가 증가함에 따라 시스템 리소스를 체계적으로 확장해야 한다. OpenProject의 성능은 단순히 CPU와 메모리뿐만 아니라, 데이터베이스와 백그라운드 워커의 처리 능력에 크게 의존한다. 다음은 사용자 규모에 따른 권장 구성 예시이다.5

  • 중규모 인스턴스 (약 500명 사용자, 중간 수준의 동시 접속)

  • Database: 2-4 vCPU / 8 GB RAM

  • Application CPU: 4 vCPU

  • Application RAM: 8 GB

  • Web Workers: 4개 워커 (각 4-8 스레드)

  • Background Workers: 2개 다중 스레드 워커

  • Disk Space: 50 GB + 첨부 파일 저장 공간

이처럼 사용자 수가 증가할수록 단순히 web 컨테이너의 수를 늘리는 수평적 확장(scale-out)뿐만 아니라, 데이터베이스 서버의 사양을 높이는 수직적 확장(scale-up)을 병행해야 한다. 또한, 이메일 알림이나 리포트 생성과 같은 백그라운드 작업의 부하가 높다면, OPENPROJECT_WORKER_PROCESSES 환경 변수를 조정하여 백그라운드 워커의 수를 늘리는 것이 전체 시스템 성능 유지에 매우 효과적이다. 이러한 확장 전략은 초기 인프라 설계 단계에서부터 고려되어야 안정적인 서비스 운영이 가능하다.

2. 배포 환경 준비 및 핵심 구성

이 장에서는 OpenProject 배포를 위한 초기 환경을 설정하고, 리버스 프록시 연동을 포함한 핵심 구성 파일들을 준비하는 과정을 단계별로 상세히 다룬다. 정확한 설정은 안정적인 시스템 운영의 초석이 된다.

2.1 단계: 공식 openproject-docker-compose 리포지토리 클론

가장 먼저, OpenProject 공식 팀이 유지보수하는 Docker Compose 설정 파일을 로컬 시스템으로 가져와야 한다. 안정적인 운영을 위해서는 항상 특정 안정(stable) 버전을 명시하여 사용하는 것이 바람직하다.

터미널에서 다음 명령어를 실행하여 최신 안정 버전(이 안내서 작성 시점 기준 stable/16)의 리포지토리를 openproject라는 디렉토리에 클론하라.3

git clone https://github.com/opf/openproject-docker-compose.git --depth=1 --branch=stable/16 openproject

여기서 사용된 옵션의 의미는 다음과 같다.

  • --branch=stable/16: 특정 안정 버전 브랜치를 지정한다. 이를 통해 예기치 않은 최신 개발 버전의 변경사항으로부터 운영 환경을 보호할 수 있다.

  • --depth=1: 전체 git 히스토리를 제외하고 가장 최신의 커밋 하나만 가져온다. 이는 다운로드 시간을 단축하고 디스크 공간을 절약하는 효율적인 방법이다.

클론이 완료되면 생성된 openproject 디렉토리로 이동한다.

cd openproject

2.2 단계: 핵심 구성 파일 준비 및 분석

이 단계에서는 OpenProject 스택의 동작을 제어하는 두 가지 핵심 파일, .envdocker-compose.override.yml을 준비하고 분석한다.

2.2.1 .env 파일 생성 및 구성

.env 파일은 데이터베이스 암호, 비밀 키 등 민감하거나 환경에 따라 달라지는 설정 변수들을 정의하는 곳이다. 예제 파일을 복사하여 자신만의 구성 파일을 생성한다.6

cp.env.example.env

이제 텍스트 편집기(예: vim, nano)를 사용하여 .env 파일을 열고, 다음의 핵심 변수들을 반드시 수정해야 한다.

  • SECRET_KEY_BASE: 이 변수는 Rails 애플리케이션의 세션 데이터 암호화 및 위변조 방지에 사용되는 매우 중요한 비밀 키다. 이 값이 노출되면 세션 탈취 등 심각한 보안 사고로 이어질 수 있다. openssl rand -hex 64와 같은 명령어를 사용하여 생성된 길고 예측 불가능한 임의의 문자열로 반드시 교체해야 한다.2

  • POSTGRES_PASSWORD: PostgreSQL 데이터베이스의 postgres 슈퍼유저 계정에 대한 암호다. 이 또한 강력하고 임의의 문자열로 설정해야 한다.3

  • DATABASE_URL: OpenProject 애플리케이션이 데이터베이스에 연결할 때 사용하는 URL이다. 이 URL에 포함된 암호는 바로 위에서 설정한 POSTGRES_PASSWORD와 반드시 일치해야 한다. 두 값이 다를 경우, web 컨테이너가 데이터베이스에 연결하지 못해 정상적으로 시작되지 않는 가장 흔한 오류의 원인이 된다.3

2.2.2 docker-compose.yml 파일 분석

docker-compose.yml 파일은 OpenProject 스택을 구성하는 모든 서비스와 그들 간의 관계, 네트워크, 볼륨 등을 정의한 청사진이다. 이 파일을 직접 수정하는 것은 권장되지 않는다. 그 이유는 git pull 명령어로 리포지토리를 업데이트할 때마다 로컬에서 수정한 내용이 공식 버전으로 덮어쓰여질 위험이 있기 때문이다.7

대신, 이 파일의 구조를 이해하는 것이 중요하다. x-op-app과 같은 YAML 앵커(&)와 에일리어스(*)는 여러 서비스(web, worker, cron 등)에 공통적으로 적용되는 설정을 재사용하여 코드의 중복을 줄이고 유지보수성을 높이는 기법이다.3 또한,volumes 섹션에 정의된 pgdataopdata는 각각 데이터베이스 데이터와 첨부 파일을 저장하는 명명된 볼륨(named volumes)으로, 컨테이너가 삭제되더라도 데이터가 영속적으로 보존되도록 보장한다.6

2.2.3 docker-compose.override.yml을 활용한 사용자 정의

모든 사용자 정의 설정은 docker-compose.override.yml이라는 별도의 파일에 작성하는 것이 Docker Compose의 모범 사례(Best Practice)이다. Docker Compose는 실행 시 docker-compose.yml 파일을 먼저 읽고, 그 위에 docker-compose.override.yml 파일의 내용을 덧씌워 최종 구성을 완성한다.9

이 방식을 사용하면 원본 docker-compose.yml 파일을 전혀 건드리지 않고도 포트 매핑 변경, 환경 변수 추가, 볼륨 설정 수정 등 필요한 부분만 선택적으로 재정의할 수 있다. 이는 향후 OpenProject 버전을 업그레이드할 때 발생할 수 있는 설정 충돌을 최소화하고, 업그레이드 과정을 매우 단순하게 만들어준다.

2.3 단계: Nginx Proxy Manager 연동을 위한 HTTP 설정

사용자 요구사항에 따라 TLS/SSL 암호화는 외부 Nginx Proxy Manager(NPM)에서 처리하고, NPM과 OpenProject 서버 간의 통신은 암호화되지 않은 HTTP를 사용해야 한다. 이를 위해 OpenProject 애플리케이션의 동작 방식을 명확하게 제어해야 한다.

2.3.1 OPENPROJECT_HTTPS=false 설정

.env 파일에 OPENPROJECT_HTTPS=false를 명시적으로 설정하라. 이 설정이 누락될 경우 심각한 연결 오류가 발생할 수 있다.2

이 설정이 필수적인 이유는 OpenProject의 기본 동작 방식과 관련이 있다.

  1. 기본 동작: OPENPROJECT_HTTPS 변수가 설정되지 않으면, OpenProject는 프로덕션 환경으로 간주하여 기본값을 true로 사용한다.

  2. HSTS 헤더 및 리디렉션: HTTPS 모드가 활성화되면, OpenProject는 모든 응답에 HSTS(HTTP Strict Transport Security) 헤더를 포함시킨다. 이 헤더는 브라우저에게 앞으로 모든 요청을 반드시 HTTPS로만 보내도록 강제한다. 또한, 서버 측에서도 들어오는 모든 HTTP 요청을 HTTPS로 리디렉션하는 로직이 작동한다.2

  3. 리버스 프록시 환경에서의 문제: NPM은 사용자와 HTTPS로 통신한 후, 내부망의 OpenProject 서버에는 HTTP로 요청을 전달한다. 이때 OpenProject가 다시 HTTPS로의 리디렉션을 시도하면, 요청이 NPM과 OpenProject 사이를 계속 맴도는 무한 리디렉션 루프(infinite redirect loop)에 빠지게 된다.

  4. 해결책: OPENPROJECT_HTTPS=false로 설정하면, OpenProject는 HSTS 헤더를 보내지 않고 HTTP 요청을 그대로 수락한다. TLS/SSL 관련 처리는 전적으로 외부 프록시의 책임이 되므로, 리버스 프록시 환경에서 정상적으로 작동할 수 있게 된다.

2.3.2 OPENPROJECT_HOST__NAME 설정

이 변수에는 사용자가 웹 브라우저 주소창에 입력하여 OpenProject에 접속하게 될 최종 도메인 이름을 설정해야 한다. 예를 들어, openproject.yourdomain.com과 같은 형식이다.2 이 값은 시스템이 이메일 알림을 보낼 때 포함되는 링크의 주소를 생성하거나, 페이지 내부에서 다른 페이지로 리디렉션할 때 절대 경로를 구성하는 등 애플리케이션의 여러 기능에서 사용된다. 만약 이 값이 실제 접속 주소와 다르게 설정되면, 이메일 링크가 깨지거나 특정 기능이 올바르게 동작하지 않을 수 있다.

2.3.3 포트 설정 (PORT)

.env 파일의 PORT 변수를 사용하여 OpenProject 스택이 호스트 머신에 노출할 포트를 지정할 수 있다. 기본값은 8080이다.7 NPM에서는 이 포트를 목적지로 설정하여 트래픽을 전달해야 한다. 예를 들어, OpenProject 서버의 IP가

192.168.1.100이고 PORT8080으로 설정했다면, NPM의 프록시 설정에서 포워딩 호스트 주소는 192.168.1.100, 포워딩 포트는 8080이 되어야 한다.

2.4 테이블 1: 주요 .env 환경 변수 상세 설명

다음 표는 OpenProject 초기 설정 시 .env 파일에서 반드시 확인하고 구성해야 할 핵심 환경 변수들을 정리한 것이다. 이 표를 체크리스트로 활용하여 설정 오류를 최소화하라.

변수명설명예시 값중요도 및 참고사항
TAG사용할 OpenProject Docker 이미지의 태그. 특정 버전을 고정하거나 stable/16과 같이 지정.16높음. 운영 안정성을 위해 최신 안정 버전을 명시적으로 지정하는 것을 권장.
SECRET_KEY_BASERails 애플리케이션의 암호화 키. 반드시 고유하고 예측 불가능한 값으로 설정.openssl rand -hex 64로 생성필수. 보안에 직결됨. 이 값이 변경되면 모든 사용자 세션이 무효화됨.
OPENPROJECT_HOST__NAME사용자가 접속하는 외부 도메인 이름.openproject.yourdomain.com필수. 이메일 링크 및 리디렉션 URL 생성에 사용.
OPENPROJECT_HTTPSHTTPS 사용 여부. 리버스 프록시에서 TLS 처리 시 false로 설정.false필수 (본 안내서). 잘못 설정 시 무한 리디렉션 오류 발생.
PORTOpenProject 스택이 호스트에 노출할 포트.8080NPM 설정과 일치해야 함.
POSTGRES_PASSWORDPostgreSQL 데이터베이스의 postgres 사용자 암호.강력한 임의의 암호필수. DATABASE_URL의 암호와 일치해야 함.
DATABASE_URLOpenProject가 DB에 연결하기 위한 URL.postgres://postgres:YOUR_PASSWORD@db/openproject필수. 포맷을 정확히 지켜야 함.
OPENPROJECT_DEFAULT__LANGUAGE기본 언어 설정.ko초기 설정 시 적용됨.
SMTP_...이메일 발송을 위한 SMTP 서버 설정 변수들.(사용자 환경에 따라 다름)알림 기능을 위해 필수적. (VI 섹션에서 상세히 다룸)

3. OpenProject 스택 배포 및 초기화

모든 구성 파일 준비가 완료되었다면, 이제 Docker Compose를 사용하여 OpenProject 스택을 실제로 배포하고 초기화할 차례다. 이 과정은 몇 가지 간단한 명령어로 이루어지지만, 각 단계의 의미를 이해하고 로그를 통해 정상적으로 진행되는지 확인하는 것이 중요하다.

3.1 단계: 컨테이너 실행

openproject 디렉토리(모든 구성 파일이 위치한 곳)에서 다음 명령어를 실행하여 정의된 모든 서비스를 백그라운드에서 시작한다.

docker compose up -d

이 명령어는 docker-compose.ymldocker-compose.override.yml 파일을 읽어 필요한 Docker 이미지를 다운로드(pull)하고, 정의된 구성에 따라 각 서비스의 컨테이너를 생성 및 실행한다.3

-d (detached) 옵션은 컨테이너들을 백그라운드에서 실행시켜 터미널을 계속 사용할 수 있게 해준다.

3.2 단계: 초기화 과정 모니터링

컨테이너들이 시작되면, 특히 최초 실행 시에는 내부적으로 중요한 초기화 과정이 진행된다. 이 과정을 실시간으로 모니터링하여 모든 것이 정상적으로 진행되는지 확인해야 한다.

다음 명령어를 사용하여 모든 컨테이너의 로그를 실시간으로 스트리밍한다.6

docker compose logs -f

로그를 관찰할 때 주목해야 할 부분은 seeder 컨테이너의 활동이다. 이 컨테이너는 다음과 같은 중요한 작업을 수행한다:

  • 데이터베이스 스키마 생성 및 최신 버전으로 마이그레이션.

  • 기본 역할(role) 및 권한(permission) 설정.

  • 초기 관리자 계정 생성.

이 과정은 시스템 사양에 따라 몇 분 정도 소요될 수 있다. 로그에서 “Database setup finished” 또는 이와 유사한 성공 메시지가 나타나고 seeder 컨테이너가 정상적으로 종료(exit 0)되면 초기화가 성공적으로 완료된 것이다.12

만약 오류가 발생한다면, 로그는 문제의 원인을 파악하는 가장 중요한 단서가 된다. 가장 흔하게 발생하는 초기 오류는 다음과 같다:

  • 데이터베이스 연결 실패: .env 파일의 POSTGRES_PASSWORDDATABASE_URL에 설정된 암호가 일치하지 않는 경우.8

  • 볼륨 권한 문제: Docker가 호스트의 특정 디렉토리에 데이터를 쓰려고 할 때 권한이 부족한 경우.7

  • 네트워크 문제: 컨테이너 간 통신이 방화벽이나 다른 네트워크 설정에 의해 차단되는 경우.

오류 메시지를 주의 깊게 읽고, II장에서 설정한 구성 파일들을 다시 한번 점검하여 문제를 해결하라.

3.3 단계: 최초 접속 및 관리자 계정 설정

모든 서비스가 정상적으로 실행되고 초기화가 완료되었다면, 이제 웹 브라우저를 통해 OpenProject에 접속할 수 있다. NPM에 설정한 도메인(예: http://openproject.yourdomain.com)으로 이동하라.

OpenProject 로그인 화면이 나타나면, 기본 관리자 계정 정보를 사용하여 로그인한다 2:

  • 사용자명: admin

  • 비밀번호: admin

최초 로그인에 성공하면 즉시 암호 변경 화면으로 리디렉션된다. 보안을 위해 기본 암호인 admin을 절대 그대로 사용해서는 안 되며, 예측하기 어려운 강력한 암호로 반드시 변경해야 한다. 이는 외부 공격으로부터 시스템을 보호하기 위한 가장 기본적인 조치다.

암호 변경 후 OpenProject의 대시보드가 나타나면, 모든 설치 및 초기화 과정이 성공적으로 완료된 것이다.

4. 기능 확장: 커스텀 플러그인 설치 심층 가이드

OpenProject의 큰 장점 중 하나는 플러그인을 통해 기능을 확장할 수 있다는 점이다. 하지만 Docker 환경에서 플러그인을 설치하는 과정은 단순히 파일을 복사하는 것 이상을 요구하며, 커스텀 Docker 이미지를 빌드하는 체계적인 접근이 필요하다. 이 장에서는 그 이유와 구체적인 절차를 심층적으로 다룬다.

4.1 개요: 왜 커스텀 이미지 빌드가 필요한가

OpenProject의 플러그인은 애플리케이션의 핵심 코드와 깊이 통합되어 동작한다. 새로운 플러그인을 추가하는 것은 RubyGems라는 의존성 관리 시스템을 통해 관리되며, 이는 애플리케이션의 핵심 의존성 목록(Gemfile)을 변경하고 bundle install 명령어를 통해 관련 라이브러리들을 새로 설치하는 과정을 포함한다.13

이러한 방식은 불변 인프라(Immutable Infrastructure) 라는 현대적인 DevOps 철학에 기반한다. 이 철학의 핵심은 한번 배포된 운영 환경(컨테이너)은 실행 중에 변경하지 않는다는 것이다. 대신, 변경 사항(예: 플러그인 추가, 라이브러리 업데이트)이 필요할 경우, 해당 변경 사항이 모두 적용된 새로운 버전의 이미지를 빌드하고, 기존 컨테이너를 이 새로운 이미지 기반의 컨테이너로 교체하여 배포한다.

이 접근법은 다음과 같은 명확한 이점을 제공한다:

  • 일관성 및 예측 가능성: 모든 환경(개발, 테스트, 운영)에서 동일한 이미지를 사용하므로 “내 PC에서는 됐는데 서버에서는 안돼“와 같은 환경 불일치 문제를 원천적으로 방지한다.

  • 안정적인 롤백: 배포에 문제가 발생했을 경우, 단순히 이전 버전의 이미지를 사용하여 컨테이너를 다시 실행하는 것만으로 빠르고 안정적인 롤백이 가능하다.

  • 버전 관리: Docker 이미지 자체를 버전 관리의 단위로 삼을 수 있어, 어떤 플러그인과 라이브러리가 설치되었는지 명확하게 추적할 수 있다.

따라서 OpenProject에서 플러그인을 설치하는 작업은 ’실행 중인 서버의 설정 변경’이 아니라, ’새로운 버전의 애플리케이션을 릴리즈’하는 개발 워크플로우로 이해하고 접근해야 한다.

4.2 단계: 빌드 환경 구성

먼저, 커스텀 이미지를 빌드하는 데 필요한 파일들을 관리할 별도의 디렉토리를 생성한다. openproject 디렉토리 내에 custom-plugins라는 이름의 디렉토리를 만드는 것을 권장한다.

mkdir custom-plugins
cd custom-plugins

이 디렉토리 안에 Gemfile.plugins라는 이름의 파일을 새로 생성한다. 이 파일은 설치하고자 하는 모든 커뮤니티 플러그인을 선언하는 역할을 한다.13

예를 들어, GitLab과의 연동을 위한 openproject-gitlab_integration 플러그인을 설치하려면 Gemfile.plugins 파일에 다음과 같이 작성한다.

Gemfile.plugins 예시:

# 플러그인들을 opf_plugins 그룹 내에 정의하는 것이 권장됨
group :opf_plugins do
# gem '플러그인명', git: '플러그인 Git 저장소 주소', branch: '사용할 브랜치'
gem 'openproject-gitlab_integration', git: 'https://github.com/btey/openproject-gitlab-integration.git', branch: 'master'
end

4.3 단계: 커스텀 Dockerfile 작성

다음으로, custom-plugins 디렉토리 안에 Dockerfile이라는 이름의 파일을 생성한다. 이 파일은 Docker에게 새로운 이미지를 어떻게 빌드해야 하는지 알려주는 레시피 역할을 한다.

Dockerfile 내용:

#.env 파일 또는 docker-compose.override.yml의 TAG 변수를 빌드 인자로 받음
ARG TAG
# 해당 태그의 공식 slim 이미지를 베이스 이미지로 사용
# slim 이미지는 프로덕션 환경에 불필요한 개발 도구들을 제외하여 이미지 크기가 작음
FROM openproject/openproject:${TAG}-slim

# 위에서 작성한 Gemfile.plugins 파일을 컨테이너 내부의 애플리케이션 루트 디렉토리(/app)로 복사
COPY Gemfile.plugins /app/

# 번들러(Bundler) 설정을 일시적으로 변경하고, 새로운 Gem을 포함하여 모든 의존성을 설치한 후, 다시 설정을 원복함
# 이는 프로덕션 환경에서의 안정적인 의존성 관리를 위한 공식 절차임
RUN bundle config unset deployment \
&& bundle install \
&& bundle config set deployment 'true'

# 플러그인 설치 후 필요한 애셋(asset) 컴파일 등 후속 작업을 실행하는 공식 스크립트
RUN./docker/prod/setup/postinstall.sh

Dockerfile은 공식 OpenProject 이미지를 기반으로, 우리가 추가한 Gemfile.plugins의 내용을 적용하여 새로운 의존성을 설치하고 필요한 후속 작업을 수행하는 과정을 정의한다.2

4.4 단계: Docker Compose 설정 수정

이제 Docker Compose가 공식 이미지 대신 우리가 빌드할 커스텀 이미지를 사용하도록 설정을 변경해야 한다. 이 작업은 docker-compose.override.yml 파일을 통해 수행한다. openproject 디렉토리(상위 디렉토리)로 돌아가서 docker-compose.override.yml 파일을 생성하거나 수정한다.

docker-compose.override.yml 예시:

version: "3.8"

services:
# x-op-app 앵커를 사용하는 모든 서비스에 대해 build 설정을 추가해야 함
web:
# 새로 빌드될 이미지에 고유한 이름과 태그를 부여 (예: my-custom-openproject:16)
image: my-custom-openproject:${TAG:-16}
build:
# Dockerfile이 위치한 디렉토리를 빌드 컨텍스트로 지정
context:./custom-plugins
args:
#.env 파일의 TAG 변수를 Dockerfile 내부의 ARG TAG로 전달
TAG: "${TAG:-16}"
worker:
image: my-custom-openproject:${TAG:-16}
build:
context:./custom-plugins
args:
TAG: "${TAG:-16}"
cron:
image: my-custom-openproject:${TAG:-16}
build:
context:./custom-plugins
args:
TAG: "${TAG:-16}"
seeder:
image: my-custom-openproject:${TAG:-16}
build:
context:./custom-plugins
args:
TAG: "${TAG:-16}"

이 설정은 web, worker, cron, seeder 서비스가 시작될 때 openproject/openproject 이미지를 Docker Hub에서 가져오는 대신, ./custom-plugins 디렉토리의 Dockerfile을 사용하여 로컬에서 my-custom-openproject라는 이름의 이미지를 빌드하도록 지시한다.14

4.5 단계: 커스텀 이미지 빌드 및 재배포

모든 설정이 완료되었다. 이제 다음 명령어를 실행하여 커스텀 이미지를 빌드한다.

docker compose build

이 명령어는 docker-compose.override.yml에 정의된 build 설정을 읽어 이미지 빌드 과정을 시작한다. 플러그인 의존성을 다운로드하고 설치하는 과정에서 다소 시간이 소요될 수 있다.

빌드가 성공적으로 완료되면, 다음 명령어를 사용하여 기존 컨테이너를 중지 및 제거하고, 새로 빌드된 커스텀 이미지를 기반으로 컨테이너를 다시 생성하여 실행한다.

docker compose up -d --force-recreate

--force-recreate 옵션은 이미 실행 중인 컨테이너가 있더라도 강제로 다시 생성하여 이미지 변경사항이 확실하게 적용되도록 보장한다.

재배포가 완료된 후, OpenProject에 관리자 계정으로 로그인하여 Administration -> Plugins 메뉴로 이동하라. 새로 설치한 ‘Gitlab Integration’ 플러그인이 목록에 나타나면 모든 과정이 성공적으로 완료된 것이다.15

5. 실무 운영 및 유지보수 전략

OpenProject를 성공적으로 배포한 후에는 안정적인 운영을 위한 체계적인 유지보수 전략이 필수적이다. 이 장에서는 데이터의 안전을 보장하는 백업 및 복원 절차, 시스템을 최신 상태로 유지하는 업그레이드 방법, 그리고 문제가 발생했을 때 신속하게 대응하기 위한 문제 해결 기법을 다룬다.

5.1 백업 (Backup)

Docker Compose 환경에서의 백업은 단일 명령으로 해결되지 않는다. 데이터의 영속성을 책임지는 두 가지 핵심 요소, 즉 데이터베이스첨부 파일 볼륨을 각각 별도로, 그러나 시점 일관성을 최대한 유지하며 백업해야 한다. 이 작업은 자동화된 스크립트를 작성하여 cron 등을 통해 정기적으로 실행하는 것이 가장 이상적이다.

5.1.1 데이터베이스 백업

실행 중인 데이터베이스 컨테이너에 직접 접근하여 pg_dump 유틸리티를 사용하는 것이 가장 안정적이고 권장되는 방법이다. 이 방식은 데이터베이스의 논리적 덤프(SQL 문)를 생성하므로, PostgreSQL 버전이 다르거나 아키텍처가 다른 시스템에도 유연하게 복원할 수 있다.16

다음은 데이터베이스를 백업하는 셸 스크립트 예시이다.

backup_db.sh:

#!/bin/bash
# 백업 파일을 저장할 디렉토리
BACKUP_DIR="/path/to/your/backups"
# 날짜와 시간을 포함한 파일명 생성 (예: openproject_db_20231027_153000.sql)
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="${BACKUP_DIR}/openproject_db_${DATE}.sql"

# docker compose ps -q db 명령으로 실행 중인 db 컨테이너의 ID를 가져옴
DB_CONTAINER_ID=$(docker compose ps -q db)

# 컨테이너 ID가 없으면 오류 출력 후 종료
if; then
echo "오류: 데이터베이스 컨테이너를 찾을 수 없습니다."
exit 1
fi

echo "데이터베이스 백업 시작: ${FILENAME}"
# docker exec를 사용하여 컨테이너 내부에서 pg_dump 실행, 결과를 호스트 파일로 리디렉션
docker exec -t "$DB_CONTAINER_ID" pg_dump -U postgres -d openproject > "$FILENAME"

echo "데이터베이스 백업 완료."

5.1.2 첨부 파일 (Assets) 볼륨 백업

프로젝트에 업로드된 모든 파일(이미지, 문서 등)은 opdata 볼륨에 저장된다. 이 볼륨의 데이터를 백업하는 방법은 볼륨 생성 방식에 따라 달라진다.

  • 바인드 마운트(Bind Mount) 방식: docker-compose.override.yml 등에서 호스트의 특정 경로(예: ./data/opdata:/var/openproject/assets)를 컨테이너에 직접 연결한 경우, 해당 호스트 경로를 tarrsync와 같은 표준 도구로 압축하거나 다른 저장소로 복사하면 된다.

  • 명명된 볼륨(Named Volume) 방식 (기본값): Docker가 관리하는 볼륨의 실제 데이터는 호스트의 특정 시스템 경로(예: /var/lib/docker/volumes/openproject_opdata/_data)에 저장되지만, 이 경로에 직접 접근하는 것은 권장되지 않는다. 대신, 임시 컨테이너에 해당 볼륨과 백업 디렉토리를 함께 마운트하여 데이터를 안전하게 복사하는 방법을 사용한다.16

다음은 명명된 볼륨을 백업하는 셸 스크립트 예시이다.

backup_assets.sh:

#!/bin/bash
# 백업 파일을 저장할 디렉토리
BACKUP_DIR="/path/to/your/backups"
# docker volume ls 명령으로 확인한 실제 볼륨 이름
VOLUME_NAME="openproject_opdata"
# 날짜와 시간을 포함한 파일명 생성
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="${BACKUP_DIR}/openproject_assets_${DATE}.tar.gz"

echo "첨부 파일 볼륨 백업 시작: ${FILENAME}"
# 경량 이미지(alpine)를 사용하여 임시 컨테이너를 실행
# -v ${VOLUME_NAME}:/data : 백업할 볼륨을 컨테이너의 /data에 마운트
# -v ${BACKUP_DIR}:/backup : 호스트의 백업 디렉토리를 컨테이너의 /backup에 마운트
# tar czf... : /data 디렉토리의 내용을 /backup 디렉토리에 압축 파일로 생성
docker run --rm -v "${VOLUME_NAME}:/data" -v "${BACKUP_DIR}:/backup" alpine tar czf "/backup/$(basename ${FILENAME})" -C /data.

echo "첨부 파일 볼륨 백업 완료."

5.2 복원 (Restore)

시스템 장애나 데이터 손상으로 인해 백업으로부터 시스템을 복원해야 하는 시나리오를 대비한 절차이다. 복원 작업은 데이터 유실을 방지하기 위해 신중하게 진행해야 한다.

  1. 1단계: OpenProject 스택 완전 중지: docker compose down 명령어로 실행 중인 모든 컨테이너를 중지하고 제거한다.

  2. 2단계: 첨부 파일 볼륨 복원: 백업해 둔 assets 압축 파일을 opdata 볼륨에 복원한다. 위 백업 스크립트와 유사한 원리로, 임시 컨테이너를 사용하여 압축을 해제할 수 있다.

docker run --rm -v "openproject_opdata:/data" -v "/path/to/your/backups:/backup" alpine tar xzf "/backup/openproject_assets_backup.tar.gz" -C /data
  1. 3단계: 데이터베이스 복원: 이 과정은 여러 단계로 나뉜다.12

  2. docker compose up -d db 명령어로 데이터베이스 컨테이너만 먼저 실행한다.

  3. docker cp /path/to/your/backups/openproject_db_backup.sql <db_container_id>:/tmp/backup.sql 명령어로 백업 SQL 파일을 컨테이너 내부로 복사한다.

  4. docker exec -it <db_container_id> psql -U postgres 명령어로 데이터베이스에 접속한다.

  5. psql 프롬프트에서 기존 데이터베이스를 삭제하고 다시 생성하여 깨끗한 상태로 만든다.

DROP DATABASE openproject;
CREATE DATABASE openproject OWNER openproject;
\q
  1. docker exec -it <db_container_id> psql -U postgres -d openproject < /tmp/backup.sql 명령어가 아닌, psql 내부에서 \i 명령어를 사용하는 것이 더 안정적일 수 있다.
docker exec -it <db_container_id> psql -U postgres -d openproject

psql 프롬프트에서 다음을 실행한다.

\i /tmp/backup.sql
\q
  1. 4단계: 전체 스택 재시작: docker compose up -d 명령어로 모든 서비스를 시작한다. OpenProject에 접속하여 데이터와 첨부 파일이 정상적으로 복원되었는지 철저히 확인한다.

5.3 업그레이드 (Upgrade)

OpenProject는 지속적으로 새로운 기능 추가와 버그 수정을 통해 업데이트된다. Docker Compose 환경에서의 업그레이드는 비교적 간단하지만, 중요한 원칙을 반드시 지켜야 한다.

가장 중요한 원칙은 메이저 버전 건너뛰기 업그레이드를 지원하지 않는다는 점이다.17 예를 들어, 현재 버전이 14.x.x라면 16.x.x로 직접 업그레이드할 수 없다. 반드시 14.x.x -> 15.x.x -> 16.x.x 순서로, 각 메이저 버전을 순차적으로 거쳐야 한다. 이는 각 메이저 버전 변경 시 포함될 수 있는 데이터베이스 스키마 마이그레이션의 호환성을 보장하기 위함이다.

마이너 버전(예: 16.0 -> 16.1)이나 패치 버전(예: 16.1.0 -> 16.1.1)으로의 업그레이드 절차는 다음과 같다 17:

  1. 백업 필수: 업그레이드 과정에서 예기치 않은 문제가 발생할 경우를 대비하여, 시작 전 반드시 전체 시스템을 백업한다.

  2. 버전 태그 수정: .env 파일의 TAG 변수를 업그레이드하고자 하는 새로운 버전으로 수정한다. (예: TAG=16.0.0 -> TAG=16.1.0)

  3. 최신 이미지 다운로드: docker compose pull 명령어를 실행하여 Docker Hub에서 새로운 버전의 공식 이미지를 다운로드한다. (커스텀 이미지를 사용하는 경우 이 단계는 생략하고 다음 단계에서 build가 자동으로 일어난다.)

  4. 컨테이너 재배포: docker compose up -d --force-recreate 명령어를 실행하여 기존 컨테이너를 중지/제거하고 새로운 이미지 기반의 컨테이너로 교체한다. 이 과정에서 seeder 컨테이너가 자동으로 실행되어 필요한 데이터베이스 마이그레이션을 수행한다.

5.4 문제 해결 (Troubleshooting)

운영 중 문제가 발생했을 때, Docker Compose가 제공하는 도구들을 활용하면 신속하게 원인을 진단하고 해결할 수 있다.

  • 로그 분석: 문제 해결의 가장 첫 번째 단계는 로그를 확인하는 것이다. docker compose logs <service_name> (예: docker compose logs web) 명령을 사용하면 특정 서비스의 로그만 필터링하여 볼 수 있어 원인 파악에 용이하다.11

  • 컨테이너 내부 접속: docker compose exec web bash와 같이 exec 명령어를 사용하면 실행 중인 컨테이너 내부에 셸로 직접 접속할 수 있다. 이를 통해 파일 시스템을 확인하거나, 네트워크 연결 상태를 점검하거나, 프로세스 목록을 확인하는 등 심층적인 진단이 가능하다.18

  • Rails 콘솔 / Rake Task 실행: 데이터베이스의 특정 데이터를 직접 조회하거나, 수동으로 관리 작업을 실행해야 할 때 유용하다.18

  • Rails 콘솔 접속: docker compose run --rm web bundle exec rails console

  • Rake Task 실행 (예: 마이그레이션 상태 확인): docker compose run --rm web bundle exec rake db:migrate:status

5.5 테이블 2: 운영 및 문제 해결을 위한 Docker Compose 명령어

다음 표는 OpenProject 운영 및 문제 해결 시 자주 사용되는 핵심 Docker Compose 명령어들을 정리한 것이다. 이 표를 통해 필요한 명령어를 빠르게 찾아 활용할 수 있다.

목적명령어설명
전체 스택 시작 (백그라운드)docker compose up -d모든 서비스를 데몬 모드로 실행.
전체 스택 중지 및 컨테이너 제거docker compose down컨테이너와 네트워크를 중지하고 제거. (볼륨은 유지됨)
실시간 로그 확인docker compose logs -f모든 컨테이너의 로그를 실시간으로 스트리밍.
특정 서비스 로그 확인docker compose logs webweb 서비스의 로그만 확인.
실행 중인 컨테이너 목록 확인docker compose ps현재 스택의 컨테이너 상태를 확인.
특정 서비스 재시작docker compose restart webweb 서비스만 재시작.
웹 컨테이너 내부 셸 접속docker compose exec web bashweb 컨테이너 내부에서 bash 셸을 실행.
Rails 콘솔 실행docker compose run --rm web bundle exec rails console데이터 조회/조작 등 고급 디버깅을 위한 콘솔 실행.
Rake Task 실행docker compose run --rm web bundle exec rake <task_name>데이터베이스 마이그레이션 등 특정 관리 작업 실행.
최신 이미지 다운로드docker compose pulldocker-compose.yml에 명시된 모든 이미지의 최신 버전을 다운로드.

6. 고급 구성 및 최적화

기본적인 설치와 운영을 넘어, OpenProject를 조직의 요구사항에 맞게 더욱 세밀하게 조정하고 성능을 최적화하기 위한 고급 구성 방법들을 알아본다. 대부분의 설정은 .env 파일이나 docker-compose.override.yml의 환경 변수를 통해 제어할 수 있다.

6.1 이메일(SMTP) 설정

프로젝트 관리 시스템에서 이메일 알림은 작업 할당, 댓글 업데이트, 마감일 알림 등 협업의 핵심적인 역할을 수행한다. 이메일 발송 기능을 활성화하려면 사용하는 SMTP 서버 정보를 OpenProject에 설정해야 한다. .env 파일에 다음의 환경 변수들을 추가하고 올바른 값으로 채워라.10

  • EMAIL_DELIVERY_METHOD="smtp": 이메일 발송 방식으로 SMTP를 사용하도록 지정한다.

  • SMTP_ADDRESS: SMTP 서버의 주소 (예: smtp.gmail.com).

  • SMTP_PORT: SMTP 서버의 포트 (예: 587).

  • SMTP_USER_NAME: SMTP 서버 인증에 사용할 사용자 이름 또는 이메일 주소.

  • SMTP_PASSWORD: 해당 계정의 암호 또는 앱 비밀번호.

  • SMTP_AUTHENTICATION: 인증 방식 (예: plain, login, cram_md5).

  • SMTP_ENABLE_STARTTLS_AUTO: TLS 암호화를 사용할지 여부. 대부분의 최신 SMTP 서버는 true를 요구한다.

  • SMTP_DOMAIN: 발신자 이메일의 도메인.

설정을 마친 후 docker compose up -d로 스택을 재시작하면 변경사항이 적용된다. 관리자 메뉴의 이메일 설정에서 테스트 메일을 발송하여 구성이 올바른지 확인할 수 있다.

6.2 LDAP 연동

많은 기업 환경에서는 중앙 집중식 사용자 관리를 위해 LDAP(Lightweight Directory Access Protocol) 또는 Active Directory를 사용한다. OpenProject는 LDAP 연동을 지원하여 기존 사내 계정으로 로그인하고 사용자 정보를 동기화할 수 있다.

초기 배포 시 LDAP 연결을 자동으로 구성하려면 OPENPROJECT_SEED_LDAP_* 형태의 환경 변수를 사용할 수 있다. SEED 접두사가 붙은 변수들은 seeder 컨테이너가 실행될 때, 즉 데이터베이스가 처음 생성되거나 db:seed Rake task가 실행될 때만 적용된다는 점에 유의해야 한다.10

예를 들어, example이라는 이름의 LDAP 연결을 설정하려면 .env 파일에 다음과 같은 변수들을 추가할 수 있다.

  • OPENPROJECT_SEED_LDAP_EXAMPLE_NAME="My Company LDAP": OpenProject UI에 표시될 연결 이름.

  • OPENPROJECT_SEED_LDAP_EXAMPLE_HOST="ldap.yourcompany.com": LDAP 서버 호스트.

  • OPENPROJECT_SEED_LDAP_EXAMPLE_PORT="389": LDAP 서버 포트.

  • OPENPROJECT_SEED_LDAP_EXAMPLE_BASEDN="dc=yourcompany,dc=com": 검색을 시작할 기본 DN(Distinguished Name).

  • OPENPROJECT_SEED_LDAP_EXAMPLE_LOGIN_ATTRIBUTE="uid": 로그인 시 사용할 사용자 속성.

  • OPENPROJECT_SEED_LDAP_EXAMPLE_FIRSTNAME_ATTRIBUTE="givenName": 이름 속성.

  • OPENPROJECT_SEED_LDAP_EXAMPLE_LASTNAME_ATTRIBUTE="sn": 성 속성.

  • OPENPROJECT_SEED_LDAP_EXAMPLE_MAIL_ATTRIBUTE="mail": 이메일 속성.

이미 운영 중인 시스템에서 이 값을 변경해도 자동으로 적용되지 않는다. 이 경우, 관리자 페이지의 LDAP 인증 설정 메뉴에서 수동으로 구성하거나, Rake task를 통해 설정을 업데이트해야 한다.

6.3 성능 최적화

사용자 규모가 커지고 작업 부하가 증가함에 따라 OpenProject의 성능을 최적화해야 할 필요가 생긴다. Docker Compose 환경에서는 특정 환경 변수를 조정하여 웹 요청 처리 용량과 백그라운드 작업 처리량을 조절할 수 있다.

  • OPENPROJECT_WEB_WORKERS: Puma 웹 서버가 생성하는 워커 프로세스의 수를 결정한다. 각 워커는 여러 스레드를 가질 수 있으며, 동시에 처리할 수 있는 웹 요청의 수를 직접적으로 결정한다. 이 값은 일반적으로 서버의 CPU 코어 수에 맞춰 설정하는 것이 좋다. 예를 들어 4코어 CPU 환경이라면 2에서 4 사이의 값으로 설정하여 테스트해볼 수 있다.5

  • OPENPROJECT_WORKER_PROCESSES: 백그라운드 작업을 처리하는 워커의 수를 결정한다. 이메일 발송, 리포팅, 파일 인덱싱 등 백그라운드 작업이 많아 지연이 발생한다면 이 값을 늘리는 것을 고려해야 한다.

이러한 변수들은 docker-compose.override.yml 파일의 webworker 서비스 environment 섹션에 추가하여 설정할 수 있다.10

docker-compose.override.yml 성능 최적화 예시:

version: "3.8"

services:
web:
environment:
# 웹 워커 수를 4개로 설정
OPENPROJECT_WEB_WORKERS: 4
worker:
environment:
# 백그라운드 워커 프로세스 수를 2개로 설정
OPENPROJECT_WORKER_PROCESSES: 2

최적의 값은 시스템의 하드웨어 사양과 실제 워크로드 패턴에 따라 달라지므로, 값을 변경한 후에는 시스템 모니터링을 통해 CPU, 메모리 사용량과 응답 시간을 관찰하며 적절한 값을 찾아가는 과정이 필요하다.

7. 결론

본 안내서는 Docker Compose를 사용하여 OpenProject를 성공적으로 설치, 구성, 확장 및 운영하는 데 필요한 포괄적인 로드맵을 제시했다. ‘one container per process’ 아키텍처의 이해부터 시작하여, 리버스 프록시 환경을 위한 정밀한 HTTP 설정, 불변 인프라 철학에 기반한 체계적인 플러그인 설치 방법, 그리고 실무 운영의 핵심인 백업, 복원, 업그레이드 전략에 이르기까지 프로덕션 환경에서 요구되는 심층적인 지식과 절차를 다루었다.

특히, docker-compose.override.yml을 활용한 설정 분리, OPENPROJECT_HTTPS=false 설정의 기술적 배경, 그리고 커스텀 이미지 빌드를 통한 플러그인 관리 방식은 단순한 설치를 넘어 안정적이고 확장 가능한 시스템을 구축하는 데 있어 핵심적인 모범 사례이다. 또한, 제공된 백업 및 복원 스크립트 예시와 단계별 업그레이드 가이드는 데이터의 안전을 보장하고 시스템을 최신 상태로 유지하는 데 실질적인 도움을 줄 것이다.

이 안내서에서 제시된 원칙과 절차를 충실히 따름으로써, 시스템 관리자와 DevOps 엔지니어는 OpenProject를 단순한 도구가 아닌, 조직의 프로젝트 관리 역량을 강화하는 안정적이고 강력한 플랫폼으로 구축하고 운영할 수 있을 것이다. 성공적인 운영의 열쇠는 초기 구성의 정확성과 지속적인 유지보수 전략의 체계적인 실행에 있음을 기억해야 한다.

8. 참고 자료

  1. Install Docker Engine on Ubuntu, https://docs.docker.com/engine/install/ubuntu/
  2. How to Install Docker on Ubuntu – Step-by-Step Guide | DigitalOcean, https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04
  3. Install Docker Engine on RHEL - Docker Docs, https://docs.docker.com/engine/install/rhel/
  4. install the Docker Compose plugin - Docker Docs, https://docs.docker.com/compose/install/linux/
  5. How install docker compose on Linux server? - Reddit, https://www.reddit.com/r/docker/comments/1iv03ec/how_install_docker_compose_on_linux_server/
  6. Comprehensive Guide: Installing Docker and Docker Compose on Windows, Linux, and macOS | by Piyush Kashyap | Medium, https://medium.com/@piyushkashyap045/comprehensive-guide-installing-docker-and-docker-compose-on-windows-linux-and-macos-a022cf82ac0b
  7. Install Docker on Linux - Seven Bridges, https://docs.sevenbridges.com/docs/install-docker-on-linux
  8. Install Docker Desktop on Windows - Docker Docs, https://docs.docker.com/desktop/setup/install/windows-install/
  9. Get Docker Desktop - Docker Docs, https://docs.docker.com/get-started/introduction/get-docker-desktop/
  10. Get Started | Docker, https://www.docker.com/get-started/
  11. OpenProject installation with Docker Compose - OpenProject, https://www.openproject.org/docs/installation-and-operations/installation/docker-compose/
  12. Installing OpenProject – with Helm Charts, packages and Docker, https://www.openproject.org/blog/installation-helm-charts-packages-docker/
  13. How to deploy the latest version of OpenProject with Docker - TechRepublic, https://www.techrepublic.com/article/how-to-install-openproject-docker/
  14. OpenProject on Docker all-in-one container - OpenProject, https://www.openproject.org/docs/installation-and-operations/installation/docker/
  15. Migrating your packaged OpenProject database to Docker, https://www.openproject.org/docs/installation-and-operations/misc/packaged-docker-migration/
  16. OpenProject advanced configuration, https://www.openproject.org/docs/installation-and-operations/configuration/
  17. How to quickly deploy the OpenProject platform as a Docker container - TechRepublic, https://www.techrepublic.com/article/how-to-quickly-deploy-the-openproject-platform-as-a-docker-container/
  18. Restoring an OpenProject backup - OpenProject, https://www.openproject.org/docs/installation-and-operations/operation/restoring/
  19. Backing up your OpenProject installation - OpenProject, https://www.openproject.org/docs/installation-and-operations/operation/backing-up/
  20. Installing OpenProject with Docker Compose - Mike Polinowski, https://mpolinowski.github.io/docs/DevOps/Provisioning/2020-10-01–installing-openproject-in-docker/2020-10-01
  21. Configuring SSL - OpenProject, https://www.openproject.org/docs/installation-and-operations/configuration/ssl/
  22. Configuring a custom web server - OpenProject, https://www.openproject.org/docs/installation-and-operations/configuration/server/
  23. Setup OpenProject With Docker & Nginx(with SSL) - datmt, https://datmt.com/management/setup-openproject-with-docker-nginxwith-ssl/
  24. Environment variables - OpenProject, https://www.openproject.org/docs/installation-and-operations/configuration/environment/
  25. Configuring outbound emails - OpenProject, https://www.openproject.org/docs/installation-and-operations/configuration/outbound-emails/
  26. Emails and notifications - OpenProject, https://www.openproject.org/docs/system-admin-guide/emails-and-notifications/
  27. dockerimages/openproject - Docker Image, https://hub.docker.com/r/dockerimages/openproject/
  28. Upgrading your OpenProject installation - OpenProject, https://www.openproject.org/docs/installation-and-operations/operation/upgrading/
  29. OpenProject 16 - PostreSQL upgrade docker compose, https://community.openproject.org/topics/19306?r=19306
  30. How to use docker compose ps command to list containers - LabEx, https://labex.io/tutorials/docker-how-to-use-docker-compose-ps-command-to-list-containers-555086
  31. docker compose ps - Docker Docs, https://docs.docker.com/reference/cli/docker/compose/ps/
  32. How Compose works - Docker Docs, https://docs.docker.com/compose/intro/compose-application-model/
  33. OpenProject development setup via docker, https://www.openproject.org/docs/development/development-environment/docker/
  34. How To Restart Containers In Docker Compose - Warp, https://www.warp.dev/terminus/docker-compose-restart
  35. The Complete Guide to docker compose restart - Last9, https://last9.io/blog/docker-compose-restart/